package com.gitee.wsl.time.schedule.collection

import com.gitee.wsl.ext.minOrNull
import com.gitee.wsl.time.schedule.AbstractTimeScheduler
import com.gitee.wsl.time.schedule.TimeScheduler
import com.gitee.wsl.time.schedule.cron.CronDateTimeScheduler
import com.gitee.wsl.time.schedule.cron.mergeCronDateTimeSchedulers
import kotlin.time.Instant
import kotlinx.datetime.TimeZone
import kotlin.time.ExperimentalTime

data class CollectionScheduler(
    internal val schedulers: MutableList<TimeScheduler>
): AbstractTimeScheduler() {
    internal constructor() : this(mutableListOf())

    /**
     * Add [kronScheduler] into its [schedulers] list
     *
     * * When [kronScheduler] is [CronDateTimeScheduler] it will merge all [CronDateTimeScheduler]s from [schedulers] list
     * and this [kronScheduler] using [mergeCronDateTimeSchedulers] function
     * * When [kronScheduler] is [CollectionKronScheduler] it this instance will include all [kronScheduler]
     * [schedulers]
     * * Otherwise [kronScheduler] will be added to [schedulers] list
     */
    fun include(kronScheduler: TimeScheduler) {
        when (kronScheduler) {
            is CronDateTimeScheduler -> {
                val resultCronDateTimes = mutableListOf(kronScheduler)
                schedulers.removeAll {
                    if (it is CronDateTimeScheduler) {
                        resultCronDateTimes.add(it)
                    } else {
                        false
                    }
                }
                schedulers.add(
                    mergeCronDateTimeSchedulers(resultCronDateTimes)
                )
            }
            is CollectionScheduler -> kronScheduler.schedulers.forEach {
                include(it)
            }
            else -> schedulers.add(kronScheduler)
        }
    }

    @OptIn(ExperimentalTime::class)
    override fun next(
        createdAtInstant: Instant,
        currentInstant: Instant,
        zoneId: TimeZone,
    ): Result<Instant> {

        val next = schedulers.map{
            it.next(createdAtInstant, currentInstant, zoneId)
        }.minOrNull()?:return throwCall()

        return Result.success(next)
    }
}